package com.wbl.train.common.handler;

import cn.hutool.core.util.StrUtil;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.wbl.train.common.exception.ApplicationException;
import com.wbl.train.common.result.R;
import com.wbl.train.common.result.ResultCode;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import java.text.ParseException;
import java.util.List;
import java.util.Set;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    // GET请求中@Valid验证参数失败后抛出异常BindException
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(BindException.class)
    public R<String> handlerBindException(BindException e) {
        StringBuilder messages = new StringBuilder(1024);
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        for (FieldError error : fieldErrors) {
            messages.append(error.getField()).append(": ").append(error.getDefaultMessage()).append("\n\r");
        }
        return R.error(ResultCode.PARAM_VALID_ERROR,messages.toString());
    }

    // @RequestParam参数校验失败后抛出异常ConstraintViolationException
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    public R<String> handlerMethodArgumentNotValidException(ConstraintViolationException e) {
        StringBuilder messages = new StringBuilder(1024);
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        for (ConstraintViolation<?> error : constraintViolations) {
            messages.append(error.getPropertyPath().toString()).append(": ").append(error.getMessage()).append("\n\r");
        }
        return R.error(ResultCode.PARAM_VALID_ERROR,messages.toString());
    }

    // @RequestBody参数校验失败后抛出异常MethodArgumentNotValidException
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
    public R<String> handlerMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        StringBuilder messages = new StringBuilder(1024);
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        for (FieldError error : fieldErrors) {
            messages.append(error.getField()).append(": ").append(error.getDefaultMessage()).append("\n\r");
        }
        return R.error(ResultCode.PARAM_VALID_ERROR,messages.toString());
    }

    // 不支持的请求方法
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R<String> httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
        return R.error(ResultCode.METHOD_NOT_SUPPORTED, ex.getMessage());
    }

    // 不支持的媒体类型
    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public R<MediaType> httpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException ex) {
        return R.error(ResultCode.MEDIA_TYPE_NOT_SUPPORTED,ex.getContentType());
    }

    // 主键重复 503
    @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
    @ExceptionHandler(DuplicateKeyException.class)
    public R<String> idHasExist(DuplicateKeyException e) {
        log.error("---DuplicateKeyException：{}",e.getMessage());
        return R.error(ResultCode.ID_HAS_EXIST,"主键重复",e.getMessage());
    }

    // sql字段没有默认值
    @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
    @ExceptionHandler(DataIntegrityViolationException.class)
    public R<String> sqlEerror(DataIntegrityViolationException e) {
        log.error("---DataIntegrityViolationException：{}",e.getMessage());
        String errorMessage = e.getMessage();
        int index = errorMessage.lastIndexOf("Field");
        errorMessage = errorMessage
                .substring(index)
                .replace("Field","字段")
                .replace("doesn't have a default value","没有默认值");
        return R.error(ResultCode.ID_HAS_EXIST,"sql错误", errorMessage);
    }

    // ClassNotFoundException
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(ClassNotFoundException.class)
    public R<String> classNotFoundException(ClassNotFoundException e) {
        log.error("---ClassNotFoundException：{}",e.getMessage());
        String errorMessage = e.getMessage();
        return R.error(ResultCode.SCHEDULE_CLASS_NOT_EXIST,"任务类不存在", errorMessage);
    }

    // ParseException
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(ParseException.class)
    public R<String> classNotFoundException(ParseException e) {
        log.error("---ParseException：{}",e.getMessage());
        String errorMessage = e.getMessage();
        return R.error(ResultCode.SCHEDULE_CLASS_NOT_EXIST,"文本解析异常", errorMessage);
    }

    // 没有发现资源 404
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(NoHandlerFoundException.class)
    public R<String> handleNoHandlerFoundException(NoHandlerFoundException e) {
        log.error("---NoHandlerFoundException：{}",e.getMessage());
        return R.error(ResultCode.NO_HANDLER_FOUND,e.getMessage());
    }

    // sentinel限流的异常 429
    @ResponseStatus(HttpStatus.TOO_MANY_REQUESTS)
    @ExceptionHandler(FlowException.class)
    public R<String> handleNoHandlerFoundException(FlowException e) {
        log.error("---FlowException：{}",e.getMessage());
        return R.error(ResultCode.SENTINEL_DEAL_BUY_TICKET,e.getMessage());
    }



    // 处理自定义异常ApplicationException
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(ApplicationException.class)
    public R<String> applicationExceptionHandler(ApplicationException e) {
        log.error("---未知业务异常：{}",e.getMessage());
        // 异常中指定的错误代码，消息按优先级合并在异常的message中
        return R.error(e.getResultCode(),e.getMessage());
    }

    // 通常在最后的方法处理最大的Exception，保证兜底
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Throwable.class)
    public R<String> exceptionHandler(Exception e) throws Exception {
//        log.info("seata全局事务ID:{}", RootContext.getXID());
//        // 如果是在一次全局事务中出现了异常，不要分装返回值，将异常抛给调用方，让调用方回滚事务
//        if(StrUtil.isNotBlank(RootContext.getXID())){
//            throw e;
//        }
        //注意，兜底方法因为拦截的都是一些未知异常，因此需要在这里输出异常信息，否则异常无任何提示
        log.error("---未知异常：{}",e.getMessage());
        e.printStackTrace();
        return R.error(ResultCode.SERVER_ERROR,e.getMessage());
    }
}
